package geektime.spring.springbucks.customerservice.support;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class CircuitBreakerAspect {

  private static final Integer THRESHOLD = 3;
  //失败次数
  private Map<String, AtomicInteger> counter = new ConcurrentHashMap<>();
  //断路保护次数
  private Map<String, AtomicInteger> breakCounter = new ConcurrentHashMap<>();

  @Around("execution(* geektime.spring.springbucks.customerservice.integration..*(..))")
  public Object doWithCircuitBreaker(ProceedingJoinPoint pjp) throws Throwable {
    String signature = pjp.getSignature().toLongString();
    log.info("Invoke {}", signature);
    Object retVal;
    try {
      if (counter.containsKey(signature)) {
        if (counter.get(signature).get() > THRESHOLD
            && breakCounter.get(signature).get() < THRESHOLD) {
          log.warn("Circuit breaker return null.break {} times.",
              breakCounter.get(signature).incrementAndGet());
          return null;
        }
      } else {
        counter.put(signature, new AtomicInteger(0));
        breakCounter.put(signature, new AtomicInteger(0));
      }
      retVal = pjp.proceed();
      counter.get(signature).set(0);
      breakCounter.get(signature).set(0);
    } catch (Throwable e) {
      log.warn("Circuit breaker counter : {},Throwable: {}",
          counter.get(signature).incrementAndGet(), e.getMessage());
      breakCounter.get(signature).set(0);
      throw e;
    }
    return retVal;
  }
}
